﻿package net.wangit.framework.dispatcher;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.wangit.Wangit;
import net.wangit.LabelUtil;
import net.wangit.Path;
import net.wangit.context.ActionContext;
import net.wangit.context.ActionContextHelper;
import net.wangit.context.ContextUtil;
import net.wangit.context.LocalContext;
import net.wangit.framework.config.BeanFactory;
import net.wangit.framework.config.ConsoleSettings;
import net.wangit.framework.config.DomainsSettings;
import net.wangit.framework.config.GlobalSettings;
import net.wangit.framework.config.Mappings;
import net.wangit.framework.config.MimeSettings;
import net.wangit.framework.config.Pages;
import net.wangit.framework.config.ProjectSettings;
import net.wangit.framework.config.UploadSettings;
import net.wangit.framework.config.WizardSettings;
import net.wangit.framework.debug.Recorder;
import net.wangit.framework.debug.Tracker;
import net.wangit.framework.license.LManager;
import net.wangit.framework.log.UserLogger;
import net.wangit.framework.session.AgileSession;
import net.wangit.framework.util.ResponseWriter;
import net.wangit.jdbc.util.DBHelper;
import net.wangit.util.Checker;
import net.wangit.util.StringUtil;
import net.wangit.view.page.PageHelper;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


@SuppressWarnings("serial")
public class DispatcherServlet extends HttpServlet {
	private static final Log log = LogFactory.getLog(DispatcherServlet.class);
	private static boolean valid;

	public void init(ServletConfig servletConfig) throws ServletException {
		super.init(servletConfig);

		Path.setRootPath(getServletContext().getRealPath("/"));
		Path.setHomePath();

		GlobalSettings.load();
		BeanFactory.load();

		ProjectSettings.instance().load();
		ConsoleSettings.instance().load();
		WizardSettings.instance().load();

		DomainsSettings.load();

		MimeSettings.load();
		UploadSettings.load();
		try {
			valid = LManager.validate();
		} catch (Exception e) {
			valid = true;
		}
	}

	public void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException {
		ActionContext ac;
		if (!(valid)) {
			return;
		}

		try {
			String domain;
			clearResources();

			ContextUtil.createContext(request, response, getServletConfig());
			ac = LocalContext.getLocalContext().getActionContext();

			String url = request.getRequestURI();

			String[] parameters = StringUtil.splitIgnoreHeadFlag(url, "/");

			// URL参数个数大于3时无效，格式：域名/单元名/Action
			if (parameters.length > 3) {
				ResponseWriter.write(LabelUtil.get(ac, "system", "url.invalid",
						url));
				return;
			}
			// URL参数个数为1时，取GlobalSetting中的域名，否则取第一个参数为域名
			domain = (parameters.length == 1) ? GlobalSettings.getContextRoot()
					: parameters[0];
			if (!DomainsSettings.isValidDomain(domain)) {
				ResponseWriter.write(LabelUtil.get(ac, "system",
						"url.domain_not_found", url));
				return;
			}
			Recorder.setEnabled(ProjectSettings.instance().isRecordEnabled(
					domain));
			Tracker.start();
			Recorder.start();

			clearUnitCache(ac);
			
			if (parameters.length == 3) {
				doAction(ac, url, parameters);
			} else {
				doView(ac, url, parameters);
			}
		} catch (RuntimeException e) {
			log.error("RuntimeException:", e);
			ac = LocalContext.getLocalContext().getActionContext();
			ResponseWriter.writeWithHtml(LabelUtil.get(ac, "system",
					"exception.unchecked"));
		} catch (Exception e) {
			log.error("Unexpected Exception:", e);
			ac = LocalContext.getLocalContext().getActionContext();
			ResponseWriter.writeWithHtml(LabelUtil.get(ac, "system",
					"exception.unexpected"));
		} finally {
			try {
				DBHelper.closeAll();
			} catch (Exception e) {
				log.error("could not close db", e);
			}
			Tracker.end();
		}
	}

	private void clearResources() {
		if (ProjectSettings.instance().isReloadI18n())
			LabelUtil.clear();
	}

	// url后可以用参数名“clear”指定清除一个或多个单元记忆的数据，多个单元之间以逗号“,”分隔
	// 格式：*.do?clear=域1.单元编号1,域2.单元编号2
	private void clearUnitCache(ActionContext ac) {
		String clearUnits = ac.getRequestParameter("clear");
		if (!(Checker.isEmpty(clearUnits))) {
			String[] units = StringUtil.split(clearUnits, ",");
			for (int i = 0; i < units.length; ++i) {
				int index = units[i].indexOf(".");
				if (index > 0) {
					String domain = units[i].substring(0, index).trim();
					String unitId = units[i].substring(index + 1).trim();
					Wangit.clearUnitCache(ac, domain, unitId);
				}
			}
		}
	}

	private void doAction(ActionContext ac, String url, String[] parameters)
			throws Exception {
		String domain = parameters[0].toLowerCase();
		String unitId = parameters[1].toLowerCase();
		String actionId = parameters[2]
				.substring(0, parameters[2].length() - 3).toLowerCase();
		try {
			if (Mappings.contains(domain, unitId, actionId)) {
				DefaultInvocation invocation = new DefaultInvocation(domain,
						unitId, actionId);
				if (Recorder.isEnabled()) {
					Recorder.record("create action objects");
				}
				invocation.invoke();
				if (Recorder.isEnabled()) {
					Recorder.record("do action");
				}
				invocation.doResult();
			} else {
				ResponseWriter.write(LabelUtil.get(ac, "system",
						"url.action_not_found", url));
			}
		} catch (Exception e) {
			throw e;
		}
	}

	private void doView(ActionContext ac, String url, String[] parameters)
			throws Exception {
		String domain = null;
		String unitId = null;
		if (parameters.length == 1) {
			domain = GlobalSettings.getContextRoot();
			unitId = parameters[0].substring(0, parameters[0].length() - 3)
					.toLowerCase();
		} else {
			domain = parameters[0].toLowerCase();
			unitId = parameters[1].substring(0, parameters[1].length() - 3)
					.toLowerCase();
		}
		AgileSession session = ActionContextHelper.getSession(ac);
		if (!Pages.contains(domain, unitId)) {
			ResponseWriter.write(LabelUtil.get(ac, "system",
					"url.page_not_found", PageHelper.getUrl(domain, unitId)));
			return;
		}
		try {
			ViewWriter.write(ac, domain, unitId);
			session.addAccessChain(url);
		} finally {
			session.reset();
			ActionContextHelper.update();
		}
	}

	public void destroy() {
		super.destroy();
		log.info("DispatcherServlet destroy ...");
		UserLogger.flush();
	}
}